package io.kiki.sba.registry.remoting.http;

import io.kiki.sba.registry.api.Callback;
import io.kiki.sba.registry.api.Channel;
import io.kiki.sba.registry.api.Client;
import io.kiki.sba.registry.common.model.store.Url;
import io.kiki.sba.registry.net.NetUtil;
import lombok.Getter;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.UriBuilder;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;


public class HttpClient implements Client {

    private static final Logger logger = LoggerFactory.getLogger(HttpClient.class);
    @Getter
    private static final HttpClient instance = new HttpClient();

    private final AtomicReference<javax.ws.rs.client.Client> client = new AtomicReference<>(null);

    private final Map<String, Channel> channels = new HashMap<>();


    public HttpClient() {
        setClient(getClient(null));
    }


    @Override
    public Channel connect(Url url) {
        try {
            HttpChannel httpChannel = new HttpChannel(getTarget(url), getClient());
            channels.put(url.buildAddressString(), httpChannel);
            return httpChannel;
        } catch (Exception e) {
            logger.error("Create jersey connect:" + url + " error!", e);
            throw new RuntimeException("Create jersey connect:" + url + " error!", e);
        }
    }

    @Override
    public Object sendSync(Url url, Object message, int timeoutMillis) {
        return null;
    }

    @Override
    public Object sendSync(Channel channel, Object message, int timeoutMillis) {
        return null;
    }

    @Override
    public void sendWithCallback(Url url, Object message, Callback callback, int timeoutMillis) {
    }

    private WebTarget getTarget(Url targetUrl) {
        return getClient().target(getBaseUri(targetUrl));
    }

    private javax.ws.rs.client.Client getClient(ClientConfig clientConfig) {
        if (clientConfig == null) {
            clientConfig = new ClientConfig();
            clientConfig.property(ClientProperties.CONNECT_TIMEOUT, 3000);
            clientConfig.property(ClientProperties.READ_TIMEOUT, 5000);
        }

        clientConfig.connectorProvider(new HttpUrlConnectorProvider());

        clientConfig.register(JacksonFeature.class);

        return ClientBuilder.newClient(clientConfig);
    }

    public javax.ws.rs.client.Client getClient() {
        return client.get();
    }

    public void setClient(final javax.ws.rs.client.Client clientIn) {
        client.getAndSet(clientIn);
    }

    public URI getBaseUri(Url targetUrl) {
        URI uri;
        try {
            uri = UriBuilder.fromUri("http://" + targetUrl.getIp() + "/").port(targetUrl.getPort()).build();
        } catch (Exception e) {
            logger.error("get server URI error!", e);
            throw new RuntimeException("get server URI error!", e);
        }
        return uri;
    }

    @Override
    public Channel getChannel(Url url) {
        Channel c = channels.get(url.buildAddressString());
        if (c == null) {
            return null;
        } else {
            if (!c.isConnected()) {
                connect(url);
            }
        }
        return c;
    }

    @Override
    public InetSocketAddress getLocalAddress() {
        return NetUtil.getLocalSocketAddress();
    }

    @Override
    public void close() {
    }

    @Override
    public boolean isClosed() {
        return false;
    }
}
